////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//			Birdry scrollBar - 2008.03.07  제작자 : 양상윤
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

package	birdry.ui {
	
	import birdry.type.EasingType;
	import com.greensock.TweenLite;
	import com.greensock.TweenMax;
	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.events.*;
	import flash.events.TimerEvent;
	import flash.utils.Timer;
	
	
	public class ScrollBar extends EventDispatcher	{
		
		public static const INIT_POSITION_TOP:String = "init_position_top";
		public static const INIT_POSITION_CURRENT:String = "init_position_current";
		public static const INIT_POSITION_BOTTOM:String = "init_position_bottom";
		public static const BT_DIRECT_MINUS:String = "-";
		public static const BT_DIRECT_PLUS:String = "+";
		
		private var _event:EventDispatcher;
		private var _content:DisplayObject;
		private var _maskMc:DisplayObject;
		private var _barBg:MovieClip;
		private var _bar:MovieClip;
		private var _autoScrollSize:Boolean;
		
		private var _contSize:Number;
		
		private var _barMin:Number;
		private var _barMax:Number;
		private var _conMin:Number;
		private var _conMax:Number;
		
		private var _autoScroll_delta:int;
		private var _scrollTimer:Timer;
		private var _intervalID:int;
		private var _scrollIng:Boolean = false;
		private var _xy:String;
		private var _wh:String;
		
		private var _barDownMousePosition:Number;
		
		public var wheelSpeed:uint = 20;
		public var wheelAble:Boolean = true;
		public var barDragFlag:Boolean;
		public var isOver:Boolean;
		public var isDown:Boolean;
		
		public function setPosition(position:String = "init_position_top"): void	{
			setScroll();
			switch( position )	{
				case ScrollBar.INIT_POSITION_TOP:
					conMove(_maskMc[_xy], 0);
				break;
				case ScrollBar.INIT_POSITION_CURRENT:
					conMove(_content[_xy], 0);
				break;
				case ScrollBar.INIT_POSITION_BOTTOM:
					conMove( -_conMax + _maskMc[_xy], 0);
				break;
			}
		}
		
		public function conMove(targetY:Number, speed:Number = 0.3, ease:Function = null):void	{
			if (ease == null)		ease = EasingType.Expo_easeOut;
			if (_xy == "y")			TweenLite.to(_content, speed, { y:conLimit(targetY), ease:ease } );
			if (_xy == "x")			TweenLite.to(_content, speed, { x:conLimit(targetY), ease:ease } );
			barHandle(conLimit(targetY), speed, ease);
		}
		
		public function setScroll(contSize:Number=0): void	{
			_contSize = (contSize == 0) ? Math.round(_content[_wh]) : contSize;
			scrollCheck();
			
			_barMax = Math.floor(_barBg[_wh] - _bar[_wh]);
			_conMin = _maskMc[_xy];
			if (_contSize < _maskMc[_wh])	{
				_conMax = _conMin;
			}	else 		{
				_conMax = _contSize - _maskMc[_wh];
			}
			if (autoScrollSize)		autoScrollSize = true;
			autoScrollSize = autoScrollSize;
		}
		
		public function ScrollBar(content:DisplayObject, maskMc:DisplayObject, barBg:MovieClip, bar:MovieClip, xy:String = "y"):void	{
			this._content = content;
			this._maskMc = maskMc;
			this._barBg = barBg;
			this._bar = bar;
			this._xy = xy;
			this._wh = (xy == "y") ? "height" : "width";
			_event = new EventDispatcher();
			setLayout();
			eventSet();
			setScroll();
			conMove(0,0);
		}
		
		private function setLayout(): void	{
			_content.mask = _maskMc;
			_bar.buttonMode = true;
			autoScrollSize = false;
		}
		
		
		private function eventSet(): void	{
			_content.addEventListener(Event.ADDED_TO_STAGE, onAdded);
			_content.addEventListener(Event.REMOVED_FROM_STAGE, onRemoved);
			_content.dispatchEvent( new Event(Event.ADDED_TO_STAGE) );
		}
		
		private function onAdded( e: Event ): void	{
			addEvent();
		}
		
		private function onRemoved( e: Event ): void	{
			removeEvent();
		}
		
		private function addEvent(): void	{
			_bar.addEventListener(MouseEvent.MOUSE_OVER, onBarOver);
			_bar.addEventListener(MouseEvent.MOUSE_OUT, onBarOut);
			_bar.addEventListener(MouseEvent.MOUSE_DOWN, onBarDown);
			//_barBg.addEventListener(MouseEvent.MOUSE_DOWN, bgClick);
			_content.stage.addEventListener(MouseEvent.MOUSE_WHEEL, wheelHandler);
		}
		
		private function removeEvent(): void	{
			_bar.removeEventListener(MouseEvent.MOUSE_OVER, onBarOver);
			_bar.removeEventListener(MouseEvent.MOUSE_OUT, onBarOut);
			_bar.removeEventListener(MouseEvent.MOUSE_DOWN, onBarDown);
			_barBg.removeEventListener(MouseEvent.MOUSE_DOWN, bgClick);
			_content.stage.removeEventListener(MouseEvent.MOUSE_WHEEL, wheelHandler);
		}
		
		private function onBarOver( e: MouseEvent ): void		{
			isOver = true;
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_OVER ) );
		}
		
		private function onBarOut( e: MouseEvent ): void	{
			isOver = false;
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_OUT ) );
		}
		
		public function autoScroll(_intervalID:int = 100):void	{
			this._intervalID = _intervalID;
			if(_contSize > _maskMc[_wh])	{
				if(_intervalID != 0)	{
					if(_scrollIng == false)	{
						_scrollIng = true;
						_scrollTimer = new Timer(_intervalID);
						_scrollTimer.addEventListener("timer", timerHandler);
						_scrollTimer.start();
						function timerHandler(event:TimerEvent):void	{
							if(_scrollIng == true)	{
								var targetY:Number = _content[_xy] - 1;
								conMove(targetY);
							}
						}
					}
				}	else	{
					_intervalID = 0;
					autoStop();
				}
			}
		}
		
		private function autoStop():void	{
			if(_scrollIng == true)	{
				_scrollIng = false;
				_scrollTimer.stop();
				_scrollTimer = null;
			}
		}
		
		public function get autoScrollSize(): Boolean	{
			return _autoScrollSize;
		}
		public function set autoScrollSize( value:Boolean ):void	{
			_autoScrollSize = value;
			var target:int = _bar[_wh];
			if (_autoScrollSize == true) {
				if (_contSize < _maskMc[_wh])	{
					target = _barBg[_wh];
					if (_wh == "height")	TweenMax.to(_bar, 0.3, { height:target, ease:EasingType.Expo_easeOut } );
					if(_wh == "width")	TweenMax.to(_bar, 0.3, { width:target, ease:EasingType.Expo_easeOut } );
				}	else	{
					target = Math.floor((_maskMc[_wh] / _contSize) * _barBg[_wh]);
					if (_wh == "height")	TweenMax.to(_bar, 0.3, { height:target, ease:EasingType.Expo_easeOut } );
					if(_wh == "width")	TweenMax.to(_bar, 0.3, { width:target, ease:EasingType.Expo_easeOut } );
				}
			}
			_barMin = _barBg[_xy] - target;
			_barMax = Math.floor(_barBg[_wh] - target);
		}
		
		private function scrollCheck():void	{
			if (_contSize > _maskMc[_wh])	{
				
				_bar.visible = true;
				_barBg.visible = true;
				addEvent();
			}	else	{
				_content[_xy] = _maskMc[_xy];
				_bar.visible = false;
				_barBg.visible = false;
				removeEvent();
			}
		}
		
		private function wheelHandler(e:MouseEvent):void	{
			if (!wheelAble)		return;
			if ( _maskMc.hitTestPoint(_content.stage.mouseX, _content.stage.mouseY))	{
				var target:Number = _content[_xy] + (e.delta * wheelSpeed);
				conMove(target);
				autoScroll(_intervalID);
			}
		}
		
		private function onBarDown(e:MouseEvent):void	{
			isDown = true;
			barDragFlag = true;
			if (_xy == "y")	{
				_barDownMousePosition = _bar.mouseY * _bar.scaleY;
				TweenLite.to(_bar, 0.1, { y:_bar[_xy], ease:EasingType.Linear_easeNone } );
			}
			if (_xy == "x")	{
				_barDownMousePosition = _bar.mouseX * _bar.scaleX;
				TweenLite.to(_bar, 0.1, { x:_bar[_xy], ease:EasingType.Linear_easeNone } );
			}
			autoStop();
			_bar.stage.addEventListener(MouseEvent.MOUSE_UP, dragStop);
			_bar.addEventListener(Event.ENTER_FRAME, dragHandler);
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_DOWN ) );
		}
		private function dragStop(e:MouseEvent):void	{
			isDown = false;
			barDragFlag = false;
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_OUT ) );
			_bar.stage.removeEventListener(MouseEvent.MOUSE_UP, dragStop);
			_bar.stopDrag();
			_bar.removeEventListener(Event.ENTER_FRAME, dragHandler);
			if (_xy == "y")	TweenLite.to(_content, 0.1, { y:_content[_xy], ease:EasingType.Linear_easeNone } );
			if(_xy == "x")	TweenLite.to(_content, 0.1, { x:_content[_xy], ease:EasingType.Linear_easeNone } );
			autoScroll(_intervalID);
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_UP ) );
		}
		private function dragHandler(e:Event):void	{
			if (_xy == "y")	{
				_bar.y = _barBg.mouseY - _barDownMousePosition;
			}	else	{
				_bar.x = _barBg.mouseX - _barDownMousePosition;
			}
			if (_bar[_xy] < _barMin+_bar[_wh])	_bar[_xy] = _barMin+_bar[_wh];
			if (_bar[_xy] > _barMax)	_bar[_xy] = _barMax;
			
			dispatchEvent(new ScrollBarEvent( ScrollBarEvent.BAR_OVER ) );
			conHandle(_bar[_xy]);
		}
		private function bgClick(e:MouseEvent):void	{
			autoStop();
			var targetY:Number = (_xy == "y") ? barLimit(_barBg[_xy] + e.localY) : barLimit(_barBg[_xy] + e.localX)
			conHandle(targetY);
			if (_xy == "y")	TweenLite.to(_bar, 0.5, { y:targetY, ease:EasingType.Expo_easeOut } );
			if(_xy == "x")	TweenLite.to(_bar, 0.5, { x:targetY, ease:EasingType.Expo_easeOut } );
		}
		
		private function barLimit(barY:Number):Number	{
			if(barY < _barBg[_xy]) barY = _barBg[_xy];
			if(barY > _barBg[_xy] + _barBg[_wh]-_bar[_wh]) barY = _barBg[_xy]+_barBg[_wh]-_bar[_wh]
			return barY;
		}
		
		private function conLimit(conY:Number):Number	{
			if (conY > _maskMc[_xy]) conY = _maskMc[_xy];
			if (conY < -_conMax + _maskMc[_xy]) {
				conY = -_conMax + _maskMc[_xy];
				autoStop();
			}
			return conY;
		}
		
		
		/// bar의 y좌표값을 이용해서 content의 y좌표를 컨트롤한다.
		private function conHandle(barY:Number, speed:Number = 0.3, ease:Function = null):void	{
			if (ease == null)	ease = EasingType.Expo_easeOut;
			var per:Number = (barY - _barBg[_xy]) / _barMax;
			if (!per)	per = 0;	// 0/숫자 = NaN	이 되는 오류처리
			var targetY:Number = -_conMax * per + _maskMc[_xy];
			
			if(_xy == "y")	TweenLite.to(_content, speed, { y:targetY, ease:ease, onComplete:completeHandler } );
			if (_xy == "x")	TweenLite.to(_content, speed, { x:targetY, ease:ease, onComplete:completeHandler } );
			dispatchEvent( new ScrollBarEvent( ScrollBarEvent.CONTENT_MOVE, targetY, speed, ease));
			
			function completeHandler():void	{
				autoScroll(_intervalID);
			}
		}
		
		/// content의 y좌표값을 이용해서 bar의 y좌표값을 컨트롤한다.
		private function barHandle(conY:Number, speed:Number = 0.3, ease:Function = null):void	{
			if (ease == null)	ease = EasingType.Expo_easeOut;
			var per:Number = (_maskMc[_xy] - conY) / _conMax;
			var targetY:Number = _barMax * per + _barBg[_xy];
			
			if (_xy == "y")	TweenLite.to(_bar, speed, { y:targetY, ease:ease } );
			if (_xy == "x")	TweenLite.to(_bar, speed, { x:targetY, ease:ease } );
			dispatchEvent( new ScrollBarEvent( ScrollBarEvent.CONTENT_MOVE, conY, speed, ease));
		}
		
		
		///-----------------			버튼 제어에 해당되는 기능		--------------------///
		private var _btDirect:String;
		private var _btAccSpeed:Number  = 1;
		public function btDown( direct:String ): void	{
			autoStop();
			_btDirect = direct;
			_content.addEventListener(Event.ENTER_FRAME, onBtDown);
		}
		
		private function onBtDown( e: Event ): void	{
			_btAccSpeed += 0.01 * ((_content.height/_maskMc.height)*100 - _btAccSpeed);
			if (_btDirect == "-")	conMove(_content[_xy] + int(_btAccSpeed));
			
			if(_btDirect == "+")	conMove(_content[_xy] - int(_btAccSpeed));
		}
		
		public function btUp(): void	{
			autoScroll(_intervalID);
			_btAccSpeed = 1;
			_content.removeEventListener(Event.ENTER_FRAME, onBtDown);
		}
		///--------------------------------------------------------------------------///
	}
}